﻿using CommonDevelop.Cache;
using CommonEngine;
using CommonFP;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace CommonDevelop.DB
{
    /// <summary>
    /// 扩展带缓存的服务
    /// </summary>
    public abstract class BaseCacheService<T, TDB, Dto> : BaseService<T, TDB>
        where T : BaseEn, new()
        where TDB : IBase<T>
        where Dto : class, new()
    {
        protected IDCSCache cache;    //涉及到读写分离的缓存要独立设计，合在这里歧义太多
        protected string cacheKey = "Cache_" + typeof(T).Name + "_To_" + typeof(Dto).Name;
        protected Func<IEnumerable<Dto>, IEnumerable<Dto>> FilterData { get; set; }
        /// <summary>
        /// 构造
        /// </summary>
        public BaseCacheService(TDB repository, IDCSCache MemoryCache)
            : base(repository)
        {
            this.cache = MemoryCache;
        }

        protected virtual IEnumerable<Dto> GetData()
        {
            try
            {
                IEnumerable<Dto> list = this.cache.GetAsync<IEnumerable<Dto>>(cacheKey).Result;
                if (list == null)
                {
                    var data = this.repository_DB.GetList();
                    list = this.GetMapList<Dto>(data);
                    if (list.Any())
                        this.cache.Add(cacheKey, list);
                }
                return list ?? Enumerable.Empty<Dto>();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        protected virtual Expression<Func<Dto, bool>> WhereDto(string keyword)
            => null;

        public override bool CleanUp()
        {
            return this.cache.Remove(cacheKey);
        }

        public override IEnumerable<TVO> GetList<TVO>(string keyword = null, bool IsDelete = false)
        {
            if (typeof(TVO).Equals(typeof(Dto)))
            {
                IEnumerable<Dto> result = Enumerable.Empty<Dto>();
                var exp = this.WhereDto(keyword);
                if (exp == null)
                {
                    result = this.GetData();
                    if (FilterData != null)
                        result = FilterData(result);
                }
                else
                {
                    result = this.GetData().Where(exp.Compile());
                    if (FilterData != null)
                        result = FilterData(result);
                }
                return result.Cast<TVO>();
            }
            else
                return this.GetList(keyword, IsDelete).MapToList<T, TVO>();
        }

        public override IEnumerable<TVO> GetPageList<TVO>(Pagination Pag, string keyword = null, bool IsDelete = false)
        {
            if (typeof(TVO).Equals(typeof(Dto)))
            {
                IEnumerable<Dto> result = Enumerable.Empty<Dto>();
                var exp = this.WhereDto(keyword);
                if (exp == null)
                    result = this.GetData();
                else
                    result = this.GetData().Where(exp.Compile());

                if (result != null && result.Any())
                {
                    if (!string.IsNullOrWhiteSpace(Pag.sidx))
                    {
                        var order_exp = Pag.sidx.GetPropertyOrderBy<Dto>();
                        bool IsAsc = Pag.sord.ToLower() == "asc";
                        if (IsAsc)
                            result = result.OrderBy(order_exp.Compile()).ToList();
                        else
                            result = result.OrderByDescending(order_exp.Compile()).ToList();
                    }
                    //由于是数据缓存，就用Linq分页
                    result = CommonOP.LinqByPage(result.ToList(), Pag.page, Pag.rows, out int count);
                    Pag.records = count;
                }

                if (FilterData != null)
                    result = FilterData(result);

                return result.Cast<TVO>();
            }
            else
                return this.GetPageList(Pag, keyword, IsDelete).MapToList<T, TVO>();
        }

        public override Option<TVO> Get<TVO>(object keyValue)
        {
            if (typeof(TVO).Equals(typeof(Dto)))
            {
                var exp = SugarBizDB.Make_DtoKeyExp<T, Dto>(keyValue);
                var result = this.GetData().FirstOrDefault(exp.Compile()).ToOption();
                return result.Map(a => a as TVO);
            }
            else
                return base.Get<TVO>(keyValue);
        }


        public override Validation<bool> Add_En(T En)
            => base.Add_En(En).Pipe(a => a.Outcome() ? this.CleanUp() : false);

        public override Validation<bool> Add_En(IEnumerable<T> EnLs)
            => base.Add_En(EnLs).Pipe(a => a.Outcome() ? this.CleanUp() : false);

        public override Validation<bool> Save_En(T En)
            => base.Save_En(En).Pipe(a => a.Outcome() ? this.CleanUp() : false);

        public override Validation<bool> Save_En(IEnumerable<T> EnLs)
            => base.Save_En(EnLs).Pipe(a => a.Outcome() ? this.CleanUp() : false);

        public override Validation<bool> VirtualDelete(object keyValue, string UserId, string UserName)
            => base.VirtualDelete(keyValue, UserId, UserName).Pipe(a => a.Outcome() ? this.CleanUp() : false);

        public override Validation<bool> Delete(object keyValue)
            => base.Delete(keyValue).Pipe(a => a.Outcome() ? this.CleanUp() : false);

        public override Validation<bool> Add<TVO>(TVO En)
            => base.Add<TVO>(En).Pipe(a => a.Outcome() ? this.CleanUp() : false);

        public override Validation<bool> Save<TVO>(TVO En)
             => base.Save<TVO>(En).Pipe(a => a.Outcome() ? this.CleanUp() : false);
    }
}
